library("tidyr")
library('ggplot2')
library('dplyr')
library("glue")

wkdir = "~/Desktop/GitHub/Obesity/NewExtractions/H9N2/timo_0.01"
setwd(wkdir)
savedir = "~/Desktop/GitHub/Obesity/NewExtractions/H9N2/timo_0.01/Output_Figures"

source("~/Desktop/GitHub/Obesity/NewExtractions/H9N2/FD_functions.R")
diet = c("Obese","Lean","Control")
dietColors = c("#FF9933","#66CCFF","#606060")
names(dietColors) = diet
DietcolScale_fill <- scale_fill_manual(name = "grp",values = dietColors)
DietcolScale <- scale_colour_manual(name = "grp",values = dietColors)

Specifying thresholds and plotting variables

cov_cut = 200
freq_cut = 0.01
pvalcut  = 0.05

ntlist = c("A","C","G","T")
SEGMENTS = c('H9N2_PB2','H9N2_PB1','H9N2_PA','H9N2_HA','H9N2_NP','H9N2_NA','H9N2_MP','H9N2_NS')

#Loading metadata This includes titer and Ct values when applicable. ND indicates qPCR was run with a negative result; 0 indicates plaque assay or HAI was run with a negative result. NA for any values indicate that data was missing. Sacrificed indicates there was no data at that time point because the ferret had already been sacrficied for pathology.

metafile = metafile = "~/Desktop/GitHub/Obesity/NewExtractions/H9N2/H9_Metadata.csv"

meta = read.csv(file=metafile,header=T,sep=",",na.strings = c(''))
meta = filter(meta, resequenced == "yes")

meta$Ct_Mgene = as.numeric(meta$Ct_Mgene)
Warning: NAs introduced by coercion
meta$titer = as.numeric(meta$titer)
Warning: NAs introduced by coercion
meta$log10_titer = as.numeric(meta$log10_titer)
Warning: NAs introduced by coercion
meta$inf_route = factor(meta$inf_route, levels = c("Index","Contact","Aerosol","Control"))

Loading in coverage file & segment size information

cov = read.csv("./avg_coverage/H9N2.coverage.csv", header = TRUE, sep = ",")

seg_sizes = "../SegmentSize.csv"
sizes = read.csv(file=seg_sizes,header=T,sep=",",na.strings = c(''))
GenomeSize = (sizes %>% filter(segment == 'H9N2_GENOME'))$SegmentSize

cov$segment = factor(cov$segment, levels = SEGMENTS)

Checking if data passes thresholds

cov_check = CoverageAcross(cov,cov_cut,70,sizes, wkdir)
Coverage cutoff is: 200x
Percentage covered cutoff is: 70%

Merging coverage check info with the rest of the metadata

meta = merge(meta, cov_check, by.x = c("sample"), by.y = c("name"), all.y = TRUE)

nrow(meta)
[1] 1536
count(meta,quality)

Loading in variant files

varfile = "./varfiles/H9N2.VariantsOnly.0.01.200.csv"

# read and rearrange the data
vars = read.csv(file=varfile,header=T,sep=",",na.strings = c(''))
vars$name = vars$sample

Rearranging variant dataframe

vdf = ArrangeVarWRep(vars)
# already have replicate data in the varfiles from running CompareReps.v2.py script
vdf = vdf[!duplicated(vdf), ] %>% droplevels()
nrow(vdf)
[1] 1781

Filtering variant df by timo binocheck

vdf$binocheck = factor(vdf$binocheck, levels = c("False","R1","R2","True"))
vdf_bino = filter(vdf, binocheck != "False")
vdf_bino = vdf_bino[!duplicated(vdf_bino), ] %>% droplevels()
nrow(vdf_bino)
[1] 1166
# this really gets rid of a lot of variants (~1000)

Filtering variant df with frequency cutoffs

vdf = filter(vdf, minorfreq1 >= freq_cut & 
               minorfreq2 >= freq_cut & 
               minor %in% ntlist &
               major %in% ntlist) %>% 
            droplevels()
# based on MAF study, reps and 0.01% cutoff was best combo
#filter each replicate separately rather than using the average

vdf = vdf[!duplicated(vdf), ] %>% droplevels()
nrow(vdf)
[1] 1702
# does not eliminate any variants here

Adding metadata

vdf = merge(vdf,meta, by = c("sample","segment"))
vdf = vdf[!duplicated(vdf), ] %>% droplevels()

vdf$segment = factor(vdf$segment, levels = SEGMENTS)

vdf = filter(vdf, inf_route == "Index" | inf_route == "Contact" | inf_route == "Control")
# ignoring aerosol for now
vdf = filter(vdf, quality == "good")
vdf = vdf[!duplicated(vdf), ] %>% droplevels()

good_names = c(levels(factor(vdf$sample)))
transmission_info = "/Users/marissaknoll/Desktop/GitHub/Obesity/NewExtractions/H9N2/TransmissionPairs.csv"
pairs = read.csv(transmission_info, header = T)
con_change = filter(vdf, stocknt != major) %>%
  filter(major %in% ntlist)
con_change = con_change[!duplicated(con_change), ]
con_change$var = paste0(con_change$ferretID,"_",con_change$segment,"_",
                        con_change$major,"_",con_change$ntpos,"_",con_change$minor)
consensus = unique(con_change$var)
length(consensus)
[1] 10
vdf$var = paste0(vdf$ferretID,"_",vdf$segment,"_",vdf$major,"_",vdf$ntpos,"_",vdf$minor)

minorvdf = filter(vdf, !(var %in% consensus))
minorvdf = minorvdf[!duplicated(minorvdf), ]
nrow(vdf) - nrow(minorvdf)
[1] 11

SNV location plots

SNVLocation = ggplot(minorvdf, aes(x = ntpos, y = ferretID)) +
  geom_point(aes(color = diet, shape = cohort)) +
  facet_grid(inf_route~segment) +
  PlotTheme1 +
  DietcolScale
print(SNVLocation)
ggsave(SNVLocation, file = "SNVLocation.pdf", path = savedir)
Saving 7.29 x 4.51 in image

# ferret 1787 doesn't have any variants??

minorvdf$var = paste0(minorvdf$segment,"_",minorvdf$major,minorvdf$ntpos,minorvdf$minor)

# Comparing to SNVs found in the stock

stock = filter(minorvdf, DPI == "Stock")
stock = stock[!duplicated(stock), ]
stocksnv = levels(factor(stock$var))
length(stocksnv)
[1] 24
ferrets = filter(minorvdf, DPI != "Stock")
ferrets = ferrets[!duplicated(ferrets), ]

shared_w_stock = ferrets %>% filter(var %in% stocksnv)
nrow(shared_w_stock)
[1] 447
ferunique = ferrets %>% filter(!(var %in% stocksnv))
nrow(ferunique)
[1] 866
stock_shared = stock[!duplicated(stock$var),] %>% ungroup() 
stock_shared = separate(stock_shared,segment, into = c("strain","CHROM"))
stock_shared$ntvar = paste0(stock_shared$major,stock_shared$ntpos,stock_shared$minor)
stock_shared$aavar = paste0(stock_shared$majoraa,stock_shared$aapos,stock_shared$minoraa)

stock_shared_smol = select(stock_shared,CHROM,ntvar,aavar) %>% droplevels()

SNV Location compared to stock

StockSharedPlot = ggplot(shared_w_stock, aes(x = ntpos, y = ferretID)) +
  geom_point(aes(color = diet, shape = cohort), size = 2) +
  facet_grid(inf_route~segment, drop = FALSE) +
  PlotTheme1 +
  DietcolScale +
  ggtitle("SNVs found in stock")
print(StockSharedPlot)
ggsave(StockSharedPlot, file = "StockSharedPlot.pdf", height = 30, width = 15, path = savedir)


FerUniquePlot = ggplot(ferunique, aes(x = ntpos, y = ferretID)) +
  geom_point(aes(color = diet, shape = cohort)) +
  facet_grid(inf_route~segment) +
  PlotTheme1 +
  DietcolScale +
  ggtitle("SNVs not found in stock")
print(FerUniquePlot)

#ggsave(FerUniquePlot, file = "FerUniquePlot.pdf", path = savedir)

Stock variation

stock_plot = ggplot(filter(shared_w_stock, inf_route != "Aerosol"), 
                    aes(x = DPI, y = minorfreq, color = ferretID)) +
  geom_point() +
  geom_line(aes(group = ferretID)) +
  facet_grid(var~diet+inf_route) +
  PlotTheme1
ggsave("stock_plot.pdf", stock_plot, width = 8, height = 20, path = savedir)

De novo SNVs

denovo = ungroup(ferrets) %>% count(var)

filter(ferrets, var == "H9N2_PB2_A2214C") %>% ungroup() %>% count(ferretID,DPI)
filter(ferrets, var == "H9N2_PB2_A2214C") %>% count(minorfreq)
# found in basically every sample with a freq of 2-5% what is this

Venn diagram of obese and lean de novo SNVs

o_var = filter(ferrets, diet == "Obese") 
o_var = unique(o_var$var)

l_var = filter(ferrets, diet == "Lean") 
l_var = unique(l_var$var)

diet_var <- list(Obese = o_var, Lean = l_var)

#DietUniqueSNVS = ggVennDiagram(diet_var)
#print(DietUniqueSNVS)
#ggsave(DietUniqueSNVS, file = "DietUniqueSNVS.pdf", path = savedir)

Obese- and lean-specific SNVs

lean = ferrets %>% 
  filter(var %in% l_var) %>% 
  filter(!(var %in% o_var)) 

lean$sample_var = paste0(lean$sample,"_",lean$var)
lean = lean[!duplicated(lean$sample_var),] 

lean$ferretID_var = paste0(lean$ferretID,"_",lean$var)
lean = lean[!duplicated(lean$ferretID_var),] 

lean = lean %>% 
  group_by(var) %>% 
  mutate(count = 1, totalsamp = sum(count))

obese = ferrets %>% 
  filter(var %in% o_var) %>% 
  filter(!(var %in% l_var)) 

obese$sample_var = paste0(obese$sample,"_",obese$var)
obese = obese[!duplicated(obese$sample_var),] 

obese$ferretID_var = paste0(obese$ferretID,"_",obese$var)
obese = obese[!duplicated(obese$ferretID_var),] 

obese = obese %>% 
  group_by(var) %>% 
  mutate(count = 1, totalsamp = sum(count))

dietunique = rbind(lean,obese)
dietunique = dietunique[!duplicated(dietunique), ] %>% droplevels()
dietunique = filter(dietunique, inf_route != "Aerosol")
  
DietUnique = ggplot(dietunique, aes(x = ntpos, y = segment)) +
  geom_point(aes(color = nonsyn, size = totalsamp)) + 
  ggtitle("Number of samples containing each variant - diet specific") +
  theme(legend.key = element_blank(),
         strip.background = element_rect(colour="black", fill="white"),
         axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  facet_grid(diet~STRAIN) +
  PlotTheme1
print(DietUnique)
ggsave(DietUnique, filename = "SegmentSNVPlot_DietUnqique.pdf", path = savedir, width = 10, height = 5)

# make new version of this figure, separating out transmission v independent ferrets
DietUnique_InfRoute = ggplot(dietunique, aes(x = ntpos, y = segment)) +
  geom_point(aes(color = nonsyn, size = totalsamp)) + 
  ggtitle("Number of samples containing each variant - diet specific") +
  theme(legend.key = element_blank(),
         strip.background = element_rect(colour="black", fill="white"),
         axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  facet_grid(diet~inf_route) +
  PlotTheme1
print(DietUnique_InfRoute)
ggsave(DietUnique_InfRoute, filename = "SegmentSNVPlot_DietUnqique_InfRoute.pdf", 
       path = savedir, width = 15, height = 10)
ggsave(DietUnique_InfRoute, filename = "SegmentSNVPlot_DietUnqique_InfRoute.png", 
       path = savedir, width = 15, height = 10)

dNdS analysis of de novo, diet unique genes

# by ferret
dNdS_denovo_ferret = dietunique %>% 
  ungroup() %>% 
  group_by(ferretID,DPI,diet,inf_route) %>% 
  count(nonsyn)

dNdS_denovo_ferret = pivot_wider(dNdS_denovo_ferret,names_from = nonsyn, values_from = n)
dNdS_denovo_ferret = select(dNdS_denovo_ferret, ferretID,DPI,nonsyn,syn)
Adding missing grouping variables: `diet`, `inf_route`
dNdS_denovo_ferret$dNdS = paste0(dNdS_denovo_ferret$nonsyn / dNdS_denovo_ferret$syn)
dNdS_denovo_ferret$dNdS = as.numeric(dNdS_denovo_ferret$dNdS)
Warning: NAs introduced by coercion
dNdS_denovo_ferret_plot = ggplot(dNdS_denovo_ferret, aes(x = DPI, y = dNdS, color = ferretID)) +
  geom_point() +
  geom_line(aes(group = ferretID)) +
  facet_grid(~diet+inf_route) +
  PlotTheme1 
print(dNdS_denovo_ferret_plot)
ggsave("dNdS_denovo_ferret.pdf", dNdS_denovo_ferret_plot, path = savedir)
Saving 7.29 x 4.51 in image
ggsave("dNdS_denovo_ferret.png", dNdS_denovo_ferret_plot, path = savedir, width = 10, height = 5)

# by ferret and gene
dNdS_denovo_ferret_gene = dietunique %>% 
  ungroup() %>% 
  group_by(ferretID,DPI,diet,inf_route,segment) %>% 
  count(nonsyn)

dNdS_denovo_ferret_gene = pivot_wider(dNdS_denovo_ferret_gene,names_from = nonsyn, values_from = n)
dNdS_denovo_ferret_gene = select(dNdS_denovo_ferret_gene, ferretID,DPI,nonsyn,syn)
Adding missing grouping variables: `diet`, `inf_route`, `segment`
dNdS_denovo_ferret_gene$dNdS = paste0(dNdS_denovo_ferret_gene$nonsyn / dNdS_denovo_ferret_gene$syn)
dNdS_denovo_ferret_gene$dNdS = as.numeric(dNdS_denovo_ferret_gene$dNdS)
Warning: NAs introduced by coercion
dNdS_denovo_ferret_gene_plot = ggplot(dNdS_denovo_ferret_gene, aes(x = DPI, y = dNdS, color = diet)) +
  geom_point() +
  geom_line(aes(group = ferretID)) +
  facet_grid(segment~diet+inf_route) +
  PlotTheme1 +
  DietcolScale
print(dNdS_denovo_ferret_gene_plot)
ggsave("dNdS_denovo_ferret_gene_plot.pdf", dNdS_denovo_ferret_gene_plot, path = savedir)
Saving 7.29 x 4.51 in image
ggsave("dNdS_denovo_ferret_gene_plot.png", dNdS_denovo_ferret_gene_plot, path = savedir)
Saving 7.29 x 4.51 in image

#ggplot(filter(dietunique, totalsamp > 1), aes(x = DPI, y = minorfreq, color = nonsyn)) +
#  geom_point() +
#  geom_line(aes(group = var)) +
#  facet_grid(ferretID~diet+inf_route) +
#  PlotTheme1 
nonsyns = filter(dietunique, nonsyn == "nonsyn" & totalsamp > 1) %>% ungroup() %>% droplevels()
nonsyns = nonsyns[!duplicated(nonsyns$var),] 

nonsyns = separate(nonsyns,segment, into = c("strain","CHROM"))
nonsyns$ntvar = paste0(nonsyns$major,nonsyns$ntpos,nonsyns$minor)
nonsyns$aavar = paste0(nonsyns$majoraa,nonsyns$aapos,nonsyns$minoraa)

nonsyns_smol = select(nonsyns,CHROM,ntvar,aavar,diet,totalsamp) %>% droplevels()
write.csv(nonsyns_smol, "nonsyns.csv")

Which ferrets have more than one de novo?

ggplot(dietunique, aes(x = ntpos, y = ferretID)) +
  geom_point(aes(color = diet)) +
  facet_grid(~segment) +
  PlotTheme1 +
  DietcolScale


ggplot(filter(dietunique, totalsamp >1), aes(x = ntpos, y = ferretID)) +
  geom_point(aes(color = diet)) +
  facet_grid(~segment) +
  PlotTheme1 +
  DietcolScale

AF of shared de novos

ggplot(filter(dietunique, totalsamp >1), aes(x = DPI, y = minorfreq)) +
  geom_point(aes(color = var)) +
  facet_grid(~ferretID) +
  PlotTheme1

SNVs shared between diet groups

shared = ferrets %>% 
  filter(var %in% o_var) %>% 
  filter(var %in% l_var) %>% 
  group_by(var) %>% 
  mutate(count = 1, totalsamp = sum(count))

SharedPlot = ggplot(shared, aes(x = ntpos, y = segment)) +
  geom_point(aes(size = totalsamp, color = nonsyn)) +
  ggtitle("Number of samples containing each variant - Shared between diet groups") +
  theme(legend.key = element_blank(),
         strip.background = element_rect(colour="black", fill="white"),
         axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  PlotTheme1
print(SharedPlot)
ggsave(SharedPlot, filename = "SegmentSNVPlot_DietShared.pdf", path = savedir)
Saving 7.29 x 4.51 in image

Minorfreq_dist = ggplot(ferrets, aes(x = minorfreq, fill = diet)) +
  geom_histogram(binwidth = 0.01) +
  PlotTheme1 +
  facet_grid(inf_route~diet) +
  DietcolScale_fill
print(Minorfreq_dist)
ggsave("Minorfreq_dist.pdf", Minorfreq_dist, path = savedir)
Saving 7.29 x 4.51 in image

# obese seem to have fewer low-frequency de novo SNVs
obese_index = filter(ferrets, diet == "Obese" & inf_route == "Index") %>% ungroup()
lean_index = filter(ferrets, diet == "Lean" & inf_route == "Index") %>% ungroup()
t.test(obese_index$minorfreq, lean_index$minorfreq)

    Welch Two Sample t-test

data:  obese_index$minorfreq and lean_index$minorfreq
t = 0.7441, df = 689.59, p-value = 0.4571
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.006659404  0.014787455
sample estimates:
 mean of x  mean of y 
0.05836836 0.05430434 
# means are not different

obese_contact = filter(ferrets, diet == "Obese" & inf_route == "Contact") %>% ungroup()
lean_contact = filter(ferrets, diet == "Lean" & inf_route == "Contact") %>% ungroup()
t.test(obese_contact$minorfreq, lean_contact$minorfreq)

    Welch Two Sample t-test

data:  obese_contact$minorfreq and lean_contact$minorfreq
t = 1.0841, df = 359.07, p-value = 0.2791
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.006779282  0.023434481
sample estimates:
 mean of x  mean of y 
0.06797971 0.05965211 
# means are not different

# QQ_Plot: compares the quantiles of two distributions, x =y suggests they are drawn from the same distribution
qqnorm(obese_index$minorfreq, main = "Obese Index - Test of Normal Distribution")

qqnorm(lean_index$minorfreq, main = "Lean Index - Test of Normal Distribution")

# neither distribution is normal
qqplot(obese_index$minorfreq,lean_index$minorfreq, xlab = "Obese Index", ylab = "Lean Index")


qqnorm(obese_contact$minorfreq, main = "Obese Contact - Test of Normal Distribution")

qqnorm(lean_contact$minorfreq, main = "Lean Contact - Test of Normal Distribution")

# neither distribution is normal
qqplot(obese_contact$minorfreq,lean_contact$minorfreq, xlab = "Obese Contact", ylab = "Lean Contact")


# Mann-Whitney-Wilcox test (Mann-Whitney U test): samples are not normally distributed and independent of each other
wilcox.test(obese_index$minorfreq,lean_index$minorfreq)

    Wilcoxon rank sum test with continuity correction

data:  obese_index$minorfreq and lean_index$minorfreq
W = 99167, p-value = 0.2296
alternative hypothesis: true location shift is not equal to 0
wilcox.test(obese_contact$minorfreq,lean_contact$minorfreq)

    Wilcoxon rank sum test with continuity correction

data:  obese_contact$minorfreq and lean_contact$minorfreq
W = 23267, p-value = 0.01817
alternative hypothesis: true location shift is not equal to 0
# distributions are not different

# Kolmogorov-Smirnov test: samples are not normally distributed and independent of each other
# "sensitive to differences in location and shape of the empirical CDFs of the two samples"
ks.test(obese_index$minorfreq,lean_index$minorfreq)

    Asymptotic two-sample Kolmogorov-Smirnov test

data:  obese_index$minorfreq and lean_index$minorfreq
D = 0.063822, p-value = 0.3584
alternative hypothesis: two-sided
ks.test(obese_contact$minorfreq,lean_contact$minorfreq)

    Asymptotic two-sample Kolmogorov-Smirnov test

data:  obese_contact$minorfreq and lean_contact$minorfreq
D = 0.17087, p-value = 0.006143
alternative hypothesis: two-sided
# distributions are not different
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkoInRpZHlyIikKbGlicmFyeSgnZ2dwbG90MicpCmxpYnJhcnkoJ2RwbHlyJykKbGlicmFyeSgiZ2x1ZSIpCgp3a2RpciA9ICJ+L0Rlc2t0b3AvR2l0SHViL09iZXNpdHkvTmV3RXh0cmFjdGlvbnMvSDlOMi90aW1vXzAuMDEiCnNldHdkKHdrZGlyKQpzYXZlZGlyID0gIn4vRGVza3RvcC9HaXRIdWIvT2Jlc2l0eS9OZXdFeHRyYWN0aW9ucy9IOU4yL3RpbW9fMC4wMS9PdXRwdXRfRmlndXJlcyIKCnNvdXJjZSgifi9EZXNrdG9wL0dpdEh1Yi9PYmVzaXR5L05ld0V4dHJhY3Rpb25zL0g5TjIvRkRfZnVuY3Rpb25zLlIiKQpgYGAKCmBgYHtyfQpkaWV0ID0gYygiT2Jlc2UiLCJMZWFuIiwiQ29udHJvbCIpCmRpZXRDb2xvcnMgPSBjKCIjRkY5OTMzIiwiIzY2Q0NGRiIsIiM2MDYwNjAiKQpuYW1lcyhkaWV0Q29sb3JzKSA9IGRpZXQKRGlldGNvbFNjYWxlX2ZpbGwgPC0gc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJncnAiLHZhbHVlcyA9IGRpZXRDb2xvcnMpCkRpZXRjb2xTY2FsZSA8LSBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiZ3JwIix2YWx1ZXMgPSBkaWV0Q29sb3JzKQpgYGAKClNwZWNpZnlpbmcgdGhyZXNob2xkcyBhbmQgcGxvdHRpbmcgdmFyaWFibGVzCmBgYHtyfQpjb3ZfY3V0ID0gMjAwCmZyZXFfY3V0ID0gMC4wMQpwdmFsY3V0ICA9IDAuMDUKCm50bGlzdCA9IGMoIkEiLCJDIiwiRyIsIlQiKQpTRUdNRU5UUyA9IGMoJ0g5TjJfUEIyJywnSDlOMl9QQjEnLCdIOU4yX1BBJywnSDlOMl9IQScsJ0g5TjJfTlAnLCdIOU4yX05BJywnSDlOMl9NUCcsJ0g5TjJfTlMnKQpgYGAKCiNMb2FkaW5nIG1ldGFkYXRhClRoaXMgaW5jbHVkZXMgdGl0ZXIgYW5kIEN0IHZhbHVlcyB3aGVuIGFwcGxpY2FibGUuIE5EIGluZGljYXRlcyBxUENSIHdhcyBydW4gd2l0aCBhIG5lZ2F0aXZlIHJlc3VsdDsgMCBpbmRpY2F0ZXMgcGxhcXVlIGFzc2F5IG9yIEhBSSB3YXMgcnVuIHdpdGggYSBuZWdhdGl2ZSByZXN1bHQuIE5BIGZvciBhbnkgdmFsdWVzIGluZGljYXRlIHRoYXQgZGF0YSB3YXMgbWlzc2luZy4gU2FjcmlmaWNlZCBpbmRpY2F0ZXMgdGhlcmUgd2FzIG5vIGRhdGEgYXQgdGhhdCB0aW1lIHBvaW50IGJlY2F1c2UgdGhlIGZlcnJldCBoYWQgYWxyZWFkeSBiZWVuIHNhY3JmaWNpZWQgZm9yIHBhdGhvbG9neS4gCmBgYHtyfQptZXRhZmlsZSA9IG1ldGFmaWxlID0gIn4vRGVza3RvcC9HaXRIdWIvT2Jlc2l0eS9OZXdFeHRyYWN0aW9ucy9IOU4yL0g5X01ldGFkYXRhLmNzdiIKCm1ldGEgPSByZWFkLmNzdihmaWxlPW1ldGFmaWxlLGhlYWRlcj1ULHNlcD0iLCIsbmEuc3RyaW5ncyA9IGMoJycpKQptZXRhID0gZmlsdGVyKG1ldGEsIHJlc2VxdWVuY2VkID09ICJ5ZXMiKQoKbWV0YSRDdF9NZ2VuZSA9IGFzLm51bWVyaWMobWV0YSRDdF9NZ2VuZSkKbWV0YSR0aXRlciA9IGFzLm51bWVyaWMobWV0YSR0aXRlcikKbWV0YSRsb2cxMF90aXRlciA9IGFzLm51bWVyaWMobWV0YSRsb2cxMF90aXRlcikKCm1ldGEkaW5mX3JvdXRlID0gZmFjdG9yKG1ldGEkaW5mX3JvdXRlLCBsZXZlbHMgPSBjKCJJbmRleCIsIkNvbnRhY3QiLCJBZXJvc29sIiwiQ29udHJvbCIpKQpgYGAKCkxvYWRpbmcgaW4gY292ZXJhZ2UgZmlsZSAmIHNlZ21lbnQgc2l6ZSBpbmZvcm1hdGlvbgpgYGB7cn0KY292ID0gcmVhZC5jc3YoIi4vYXZnX2NvdmVyYWdlL0g5TjIuY292ZXJhZ2UuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQoKc2VnX3NpemVzID0gIi4uL1NlZ21lbnRTaXplLmNzdiIKc2l6ZXMgPSByZWFkLmNzdihmaWxlPXNlZ19zaXplcyxoZWFkZXI9VCxzZXA9IiwiLG5hLnN0cmluZ3MgPSBjKCcnKSkKR2Vub21lU2l6ZSA9IChzaXplcyAlPiUgZmlsdGVyKHNlZ21lbnQgPT0gJ0g5TjJfR0VOT01FJykpJFNlZ21lbnRTaXplCgpjb3Ykc2VnbWVudCA9IGZhY3Rvcihjb3Ykc2VnbWVudCwgbGV2ZWxzID0gU0VHTUVOVFMpCmBgYAoKQ2hlY2tpbmcgaWYgZGF0YSBwYXNzZXMgdGhyZXNob2xkcwpgYGB7cn0KY292X2NoZWNrID0gQ292ZXJhZ2VBY3Jvc3MoY292LGNvdl9jdXQsNzAsc2l6ZXMsIHdrZGlyKQpgYGAKCk1lcmdpbmcgY292ZXJhZ2UgY2hlY2sgaW5mbyB3aXRoIHRoZSByZXN0IG9mIHRoZSBtZXRhZGF0YQpgYGB7cn0KbWV0YSA9IG1lcmdlKG1ldGEsIGNvdl9jaGVjaywgYnkueCA9IGMoInNhbXBsZSIpLCBieS55ID0gYygibmFtZSIpLCBhbGwueSA9IFRSVUUpCgpucm93KG1ldGEpCmNvdW50KG1ldGEscXVhbGl0eSkKYGBgCgpMb2FkaW5nIGluIHZhcmlhbnQgZmlsZXMKYGBge3J9CnZhcmZpbGUgPSAiLi92YXJmaWxlcy9IOU4yLlZhcmlhbnRzT25seS4wLjAxLjIwMC5jc3YiCgojIHJlYWQgYW5kIHJlYXJyYW5nZSB0aGUgZGF0YQp2YXJzID0gcmVhZC5jc3YoZmlsZT12YXJmaWxlLGhlYWRlcj1ULHNlcD0iLCIsbmEuc3RyaW5ncyA9IGMoJycpKQp2YXJzJG5hbWUgPSB2YXJzJHNhbXBsZQpgYGAKClJlYXJyYW5naW5nIHZhcmlhbnQgZGF0YWZyYW1lCmBgYHtyfQp2ZGYgPSBBcnJhbmdlVmFyV1JlcCh2YXJzKQojIGFscmVhZHkgaGF2ZSByZXBsaWNhdGUgZGF0YSBpbiB0aGUgdmFyZmlsZXMgZnJvbSBydW5uaW5nIENvbXBhcmVSZXBzLnYyLnB5IHNjcmlwdAp2ZGYgPSB2ZGZbIWR1cGxpY2F0ZWQodmRmKSwgXSAlPiUgZHJvcGxldmVscygpCm5yb3codmRmKQpgYGAKCkZpbHRlcmluZyB2YXJpYW50IGRmIGJ5IHRpbW8gYmlub2NoZWNrCmBgYHtyfQp2ZGYkYmlub2NoZWNrID0gZmFjdG9yKHZkZiRiaW5vY2hlY2ssIGxldmVscyA9IGMoIkZhbHNlIiwiUjEiLCJSMiIsIlRydWUiKSkKdmRmX2Jpbm8gPSBmaWx0ZXIodmRmLCBiaW5vY2hlY2sgIT0gIkZhbHNlIikKdmRmX2Jpbm8gPSB2ZGZfYmlub1shZHVwbGljYXRlZCh2ZGZfYmlubyksIF0gJT4lIGRyb3BsZXZlbHMoKQpucm93KHZkZl9iaW5vKQojIHRoaXMgcmVhbGx5IGdldHMgcmlkIG9mIGEgbG90IG9mIHZhcmlhbnRzICh+MTAwMCkKYGBgCgpGaWx0ZXJpbmcgdmFyaWFudCBkZiB3aXRoIGZyZXF1ZW5jeSBjdXRvZmZzCmBgYHtyfQp2ZGYgPSBmaWx0ZXIodmRmLCBtaW5vcmZyZXExID49IGZyZXFfY3V0ICYgCiAgICAgICAgICAgICAgIG1pbm9yZnJlcTIgPj0gZnJlcV9jdXQgJiAKICAgICAgICAgICAgICAgbWlub3IgJWluJSBudGxpc3QgJgogICAgICAgICAgICAgICBtYWpvciAlaW4lIG50bGlzdCkgJT4lIAogICAgICAgICAgICBkcm9wbGV2ZWxzKCkKIyBiYXNlZCBvbiBNQUYgc3R1ZHksIHJlcHMgYW5kIDAuMDElIGN1dG9mZiB3YXMgYmVzdCBjb21ibwojZmlsdGVyIGVhY2ggcmVwbGljYXRlIHNlcGFyYXRlbHkgcmF0aGVyIHRoYW4gdXNpbmcgdGhlIGF2ZXJhZ2UKCnZkZiA9IHZkZlshZHVwbGljYXRlZCh2ZGYpLCBdICU+JSBkcm9wbGV2ZWxzKCkKbnJvdyh2ZGYpCiMgZG9lcyBub3QgZWxpbWluYXRlIGFueSB2YXJpYW50cyBoZXJlCmBgYAoKQWRkaW5nIG1ldGFkYXRhCmBgYHtyfQp2ZGYgPSBtZXJnZSh2ZGYsbWV0YSwgYnkgPSBjKCJzYW1wbGUiLCJzZWdtZW50IikpCnZkZiA9IHZkZlshZHVwbGljYXRlZCh2ZGYpLCBdICU+JSBkcm9wbGV2ZWxzKCkKCnZkZiRzZWdtZW50ID0gZmFjdG9yKHZkZiRzZWdtZW50LCBsZXZlbHMgPSBTRUdNRU5UUykKCnZkZiA9IGZpbHRlcih2ZGYsIGluZl9yb3V0ZSA9PSAiSW5kZXgiIHwgaW5mX3JvdXRlID09ICJDb250YWN0IiB8IGluZl9yb3V0ZSA9PSAiQ29udHJvbCIpCiMgaWdub3JpbmcgYWVyb3NvbCBmb3Igbm93CmBgYAoKYGBge3J9CnZkZiA9IGZpbHRlcih2ZGYsIHF1YWxpdHkgPT0gImdvb2QiKQp2ZGYgPSB2ZGZbIWR1cGxpY2F0ZWQodmRmKSwgXSAlPiUgZHJvcGxldmVscygpCgpnb29kX25hbWVzID0gYyhsZXZlbHMoZmFjdG9yKHZkZiRzYW1wbGUpKSkKYGBgCgpgYGB7cn0KdHJhbnNtaXNzaW9uX2luZm8gPSAiL1VzZXJzL21hcmlzc2Frbm9sbC9EZXNrdG9wL0dpdEh1Yi9PYmVzaXR5L05ld0V4dHJhY3Rpb25zL0g5TjIvVHJhbnNtaXNzaW9uUGFpcnMuY3N2IgpwYWlycyA9IHJlYWQuY3N2KHRyYW5zbWlzc2lvbl9pbmZvLCBoZWFkZXIgPSBUKQpgYGAKCmBgYHtyfQpjb25fY2hhbmdlID0gZmlsdGVyKHZkZiwgc3RvY2tudCAhPSBtYWpvcikgJT4lCiAgZmlsdGVyKG1ham9yICVpbiUgbnRsaXN0KQpjb25fY2hhbmdlID0gY29uX2NoYW5nZVshZHVwbGljYXRlZChjb25fY2hhbmdlKSwgXQpjb25fY2hhbmdlJHZhciA9IHBhc3RlMChjb25fY2hhbmdlJGZlcnJldElELCJfIixjb25fY2hhbmdlJHNlZ21lbnQsIl8iLAogICAgICAgICAgICAgICAgICAgICAgICBjb25fY2hhbmdlJG1ham9yLCJfIixjb25fY2hhbmdlJG50cG9zLCJfIixjb25fY2hhbmdlJG1pbm9yKQpjb25zZW5zdXMgPSB1bmlxdWUoY29uX2NoYW5nZSR2YXIpCmxlbmd0aChjb25zZW5zdXMpCmBgYAoKYGBge3J9CnZkZiR2YXIgPSBwYXN0ZTAodmRmJGZlcnJldElELCJfIix2ZGYkc2VnbWVudCwiXyIsdmRmJG1ham9yLCJfIix2ZGYkbnRwb3MsIl8iLHZkZiRtaW5vcikKCm1pbm9ydmRmID0gZmlsdGVyKHZkZiwgISh2YXIgJWluJSBjb25zZW5zdXMpKQptaW5vcnZkZiA9IG1pbm9ydmRmWyFkdXBsaWNhdGVkKG1pbm9ydmRmKSwgXQpucm93KHZkZikgLSBucm93KG1pbm9ydmRmKQpgYGAKClNOViBsb2NhdGlvbiBwbG90cwpgYGB7cn0KU05WTG9jYXRpb24gPSBnZ3Bsb3QobWlub3J2ZGYsIGFlcyh4ID0gbnRwb3MsIHkgPSBmZXJyZXRJRCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRpZXQsIHNoYXBlID0gY29ob3J0KSkgKwogIGZhY2V0X2dyaWQoaW5mX3JvdXRlfnNlZ21lbnQpICsKICBQbG90VGhlbWUxICsKICBEaWV0Y29sU2NhbGUKcHJpbnQoU05WTG9jYXRpb24pCmdnc2F2ZShTTlZMb2NhdGlvbiwgZmlsZSA9ICJTTlZMb2NhdGlvbi5wZGYiLCBwYXRoID0gc2F2ZWRpcikKIyBmZXJyZXQgMTc4NyBkb2Vzbid0IGhhdmUgYW55IHZhcmlhbnRzPz8KCm1pbm9ydmRmJHZhciA9IHBhc3RlMChtaW5vcnZkZiRzZWdtZW50LCJfIixtaW5vcnZkZiRtYWpvcixtaW5vcnZkZiRudHBvcyxtaW5vcnZkZiRtaW5vcikKCiMgQ29tcGFyaW5nIHRvIFNOVnMgZm91bmQgaW4gdGhlIHN0b2NrCgpzdG9jayA9IGZpbHRlcihtaW5vcnZkZiwgRFBJID09ICJTdG9jayIpCnN0b2NrID0gc3RvY2tbIWR1cGxpY2F0ZWQoc3RvY2spLCBdCnN0b2Nrc252ID0gbGV2ZWxzKGZhY3RvcihzdG9jayR2YXIpKQpsZW5ndGgoc3RvY2tzbnYpCgpmZXJyZXRzID0gZmlsdGVyKG1pbm9ydmRmLCBEUEkgIT0gIlN0b2NrIikKZmVycmV0cyA9IGZlcnJldHNbIWR1cGxpY2F0ZWQoZmVycmV0cyksIF0KCnNoYXJlZF93X3N0b2NrID0gZmVycmV0cyAlPiUgZmlsdGVyKHZhciAlaW4lIHN0b2Nrc252KQpucm93KHNoYXJlZF93X3N0b2NrKQpmZXJ1bmlxdWUgPSBmZXJyZXRzICU+JSBmaWx0ZXIoISh2YXIgJWluJSBzdG9ja3NudikpCm5yb3coZmVydW5pcXVlKQpgYGAKYGBge3J9CnN0b2NrX3NoYXJlZCA9IHN0b2NrWyFkdXBsaWNhdGVkKHN0b2NrJHZhciksXSAlPiUgdW5ncm91cCgpIApzdG9ja19zaGFyZWQgPSBzZXBhcmF0ZShzdG9ja19zaGFyZWQsc2VnbWVudCwgaW50byA9IGMoInN0cmFpbiIsIkNIUk9NIikpCnN0b2NrX3NoYXJlZCRudHZhciA9IHBhc3RlMChzdG9ja19zaGFyZWQkbWFqb3Isc3RvY2tfc2hhcmVkJG50cG9zLHN0b2NrX3NoYXJlZCRtaW5vcikKc3RvY2tfc2hhcmVkJGFhdmFyID0gcGFzdGUwKHN0b2NrX3NoYXJlZCRtYWpvcmFhLHN0b2NrX3NoYXJlZCRhYXBvcyxzdG9ja19zaGFyZWQkbWlub3JhYSkKCnN0b2NrX3NoYXJlZF9zbW9sID0gc2VsZWN0KHN0b2NrX3NoYXJlZCxDSFJPTSxudHZhcixhYXZhcikgJT4lIGRyb3BsZXZlbHMoKQpgYGAKClNOViBMb2NhdGlvbiBjb21wYXJlZCB0byBzdG9jawpgYGB7cn0KU3RvY2tTaGFyZWRQbG90ID0gZ2dwbG90KHNoYXJlZF93X3N0b2NrLCBhZXMoeCA9IG50cG9zLCB5ID0gZmVycmV0SUQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkaWV0LCBzaGFwZSA9IGNvaG9ydCksIHNpemUgPSAyKSArCiAgZmFjZXRfZ3JpZChpbmZfcm91dGV+c2VnbWVudCwgZHJvcCA9IEZBTFNFKSArCiAgUGxvdFRoZW1lMSArCiAgRGlldGNvbFNjYWxlICsKICBnZ3RpdGxlKCJTTlZzIGZvdW5kIGluIHN0b2NrIikKcHJpbnQoU3RvY2tTaGFyZWRQbG90KQpnZ3NhdmUoU3RvY2tTaGFyZWRQbG90LCBmaWxlID0gIlN0b2NrU2hhcmVkUGxvdC5wZGYiLCBoZWlnaHQgPSAzMCwgd2lkdGggPSAxNSwgcGF0aCA9IHNhdmVkaXIpCgpGZXJVbmlxdWVQbG90ID0gZ2dwbG90KGZlcnVuaXF1ZSwgYWVzKHggPSBudHBvcywgeSA9IGZlcnJldElEKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZGlldCwgc2hhcGUgPSBjb2hvcnQpKSArCiAgZmFjZXRfZ3JpZChpbmZfcm91dGV+c2VnbWVudCkgKwogIFBsb3RUaGVtZTEgKwogIERpZXRjb2xTY2FsZSArCiAgZ2d0aXRsZSgiU05WcyBub3QgZm91bmQgaW4gc3RvY2siKQpwcmludChGZXJVbmlxdWVQbG90KQojZ2dzYXZlKEZlclVuaXF1ZVBsb3QsIGZpbGUgPSAiRmVyVW5pcXVlUGxvdC5wZGYiLCBwYXRoID0gc2F2ZWRpcikKYGBgCgpTdG9jayB2YXJpYXRpb24KYGBge3J9CnN0b2NrX3Bsb3QgPSBnZ3Bsb3QoZmlsdGVyKHNoYXJlZF93X3N0b2NrLCBpbmZfcm91dGUgIT0gIkFlcm9zb2wiKSwgCiAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBEUEksIHkgPSBtaW5vcmZyZXEsIGNvbG9yID0gZmVycmV0SUQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gZmVycmV0SUQpKSArCiAgZmFjZXRfZ3JpZCh2YXJ+ZGlldCtpbmZfcm91dGUpICsKICBQbG90VGhlbWUxCmdnc2F2ZSgic3RvY2tfcGxvdC5wZGYiLCBzdG9ja19wbG90LCB3aWR0aCA9IDgsIGhlaWdodCA9IDIwLCBwYXRoID0gc2F2ZWRpcikKYGBgCgpEZSBub3ZvIFNOVnMKYGBge3J9CmRlbm92byA9IHVuZ3JvdXAoZmVycmV0cykgJT4lIGNvdW50KHZhcikKCmZpbHRlcihmZXJyZXRzLCB2YXIgPT0gIkg5TjJfUEIyX0EyMjE0QyIpICU+JSB1bmdyb3VwKCkgJT4lIGNvdW50KGZlcnJldElELERQSSkKZmlsdGVyKGZlcnJldHMsIHZhciA9PSAiSDlOMl9QQjJfQTIyMTRDIikgJT4lIGNvdW50KG1pbm9yZnJlcSkKIyBmb3VuZCBpbiBiYXNpY2FsbHkgZXZlcnkgc2FtcGxlIHdpdGggYSBmcmVxIG9mIDItNSUgd2hhdCBpcyB0aGlzCmBgYApWZW5uIGRpYWdyYW0gb2Ygb2Jlc2UgYW5kIGxlYW4gZGUgbm92byBTTlZzCmBgYHtyfQpvX3ZhciA9IGZpbHRlcihmZXJyZXRzLCBkaWV0ID09ICJPYmVzZSIpIApvX3ZhciA9IHVuaXF1ZShvX3ZhciR2YXIpCgpsX3ZhciA9IGZpbHRlcihmZXJyZXRzLCBkaWV0ID09ICJMZWFuIikgCmxfdmFyID0gdW5pcXVlKGxfdmFyJHZhcikKCmRpZXRfdmFyIDwtIGxpc3QoT2Jlc2UgPSBvX3ZhciwgTGVhbiA9IGxfdmFyKQoKI0RpZXRVbmlxdWVTTlZTID0gZ2dWZW5uRGlhZ3JhbShkaWV0X3ZhcikKI3ByaW50KERpZXRVbmlxdWVTTlZTKQojZ2dzYXZlKERpZXRVbmlxdWVTTlZTLCBmaWxlID0gIkRpZXRVbmlxdWVTTlZTLnBkZiIsIHBhdGggPSBzYXZlZGlyKQpgYGAKCk9iZXNlLSBhbmQgbGVhbi1zcGVjaWZpYyBTTlZzCmBgYHtyfQpsZWFuID0gZmVycmV0cyAlPiUgCiAgZmlsdGVyKHZhciAlaW4lIGxfdmFyKSAlPiUgCiAgZmlsdGVyKCEodmFyICVpbiUgb192YXIpKSAKCmxlYW4kc2FtcGxlX3ZhciA9IHBhc3RlMChsZWFuJHNhbXBsZSwiXyIsbGVhbiR2YXIpCmxlYW4gPSBsZWFuWyFkdXBsaWNhdGVkKGxlYW4kc2FtcGxlX3ZhciksXSAKCmxlYW4kZmVycmV0SURfdmFyID0gcGFzdGUwKGxlYW4kZmVycmV0SUQsIl8iLGxlYW4kdmFyKQpsZWFuID0gbGVhblshZHVwbGljYXRlZChsZWFuJGZlcnJldElEX3ZhciksXSAKCmxlYW4gPSBsZWFuICU+JSAKICBncm91cF9ieSh2YXIpICU+JSAKICBtdXRhdGUoY291bnQgPSAxLCB0b3RhbHNhbXAgPSBzdW0oY291bnQpKQoKb2Jlc2UgPSBmZXJyZXRzICU+JSAKICBmaWx0ZXIodmFyICVpbiUgb192YXIpICU+JSAKICBmaWx0ZXIoISh2YXIgJWluJSBsX3ZhcikpIAoKb2Jlc2Ukc2FtcGxlX3ZhciA9IHBhc3RlMChvYmVzZSRzYW1wbGUsIl8iLG9iZXNlJHZhcikKb2Jlc2UgPSBvYmVzZVshZHVwbGljYXRlZChvYmVzZSRzYW1wbGVfdmFyKSxdIAoKb2Jlc2UkZmVycmV0SURfdmFyID0gcGFzdGUwKG9iZXNlJGZlcnJldElELCJfIixvYmVzZSR2YXIpCm9iZXNlID0gb2Jlc2VbIWR1cGxpY2F0ZWQob2Jlc2UkZmVycmV0SURfdmFyKSxdIAoKb2Jlc2UgPSBvYmVzZSAlPiUgCiAgZ3JvdXBfYnkodmFyKSAlPiUgCiAgbXV0YXRlKGNvdW50ID0gMSwgdG90YWxzYW1wID0gc3VtKGNvdW50KSkKCmRpZXR1bmlxdWUgPSByYmluZChsZWFuLG9iZXNlKQpkaWV0dW5pcXVlID0gZGlldHVuaXF1ZVshZHVwbGljYXRlZChkaWV0dW5pcXVlKSwgXSAlPiUgZHJvcGxldmVscygpCmRpZXR1bmlxdWUgPSBmaWx0ZXIoZGlldHVuaXF1ZSwgaW5mX3JvdXRlICE9ICJBZXJvc29sIikKICAKRGlldFVuaXF1ZSA9IGdncGxvdChkaWV0dW5pcXVlLCBhZXMoeCA9IG50cG9zLCB5ID0gc2VnbWVudCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IG5vbnN5biwgc2l6ZSA9IHRvdGFsc2FtcCkpICsgCiAgZ2d0aXRsZSgiTnVtYmVyIG9mIHNhbXBsZXMgY29udGFpbmluZyBlYWNoIHZhcmlhbnQgLSBkaWV0IHNwZWNpZmljIikgKwogIHRoZW1lKGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIiksCiAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkgKwogIGZhY2V0X2dyaWQoZGlldH5TVFJBSU4pICsKICBQbG90VGhlbWUxCnByaW50KERpZXRVbmlxdWUpCmdnc2F2ZShEaWV0VW5pcXVlLCBmaWxlbmFtZSA9ICJTZWdtZW50U05WUGxvdF9EaWV0VW5xaXF1ZS5wZGYiLCBwYXRoID0gc2F2ZWRpciwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNSkKYGBgCgpgYGB7cn0KIyBtYWtlIG5ldyB2ZXJzaW9uIG9mIHRoaXMgZmlndXJlLCBzZXBhcmF0aW5nIG91dCB0cmFuc21pc3Npb24gdiBpbmRlcGVuZGVudCBmZXJyZXRzCkRpZXRVbmlxdWVfSW5mUm91dGUgPSBnZ3Bsb3QoZGlldHVuaXF1ZSwgYWVzKHggPSBudHBvcywgeSA9IHNlZ21lbnQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBub25zeW4sIHNpemUgPSB0b3RhbHNhbXApKSArIAogIGdndGl0bGUoIk51bWJlciBvZiBzYW1wbGVzIGNvbnRhaW5pbmcgZWFjaCB2YXJpYW50IC0gZGlldCBzcGVjaWZpYyIpICsKICB0aGVtZShsZWdlbmQua2V5ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIpLAogICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpICsKICBmYWNldF9ncmlkKGRpZXR+aW5mX3JvdXRlKSArCiAgUGxvdFRoZW1lMQpwcmludChEaWV0VW5pcXVlX0luZlJvdXRlKQpnZ3NhdmUoRGlldFVuaXF1ZV9JbmZSb3V0ZSwgZmlsZW5hbWUgPSAiU2VnbWVudFNOVlBsb3RfRGlldFVucWlxdWVfSW5mUm91dGUucGRmIiwgCiAgICAgICBwYXRoID0gc2F2ZWRpciwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gMTApCmdnc2F2ZShEaWV0VW5pcXVlX0luZlJvdXRlLCBmaWxlbmFtZSA9ICJTZWdtZW50U05WUGxvdF9EaWV0VW5xaXF1ZV9JbmZSb3V0ZS5wbmciLCAKICAgICAgIHBhdGggPSBzYXZlZGlyLCB3aWR0aCA9IDE1LCBoZWlnaHQgPSAxMCkKYGBgCgpkTmRTIGFuYWx5c2lzIG9mIGRlIG5vdm8sIGRpZXQgdW5pcXVlIGdlbmVzCmBgYHtyfQojIGJ5IGZlcnJldApkTmRTX2Rlbm92b19mZXJyZXQgPSBkaWV0dW5pcXVlICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KGZlcnJldElELERQSSxkaWV0LGluZl9yb3V0ZSkgJT4lIAogIGNvdW50KG5vbnN5bikKCmROZFNfZGVub3ZvX2ZlcnJldCA9IHBpdm90X3dpZGVyKGROZFNfZGVub3ZvX2ZlcnJldCxuYW1lc19mcm9tID0gbm9uc3luLCB2YWx1ZXNfZnJvbSA9IG4pCmROZFNfZGVub3ZvX2ZlcnJldCA9IHNlbGVjdChkTmRTX2Rlbm92b19mZXJyZXQsIGZlcnJldElELERQSSxub25zeW4sc3luKQpkTmRTX2Rlbm92b19mZXJyZXQkZE5kUyA9IHBhc3RlMChkTmRTX2Rlbm92b19mZXJyZXQkbm9uc3luIC8gZE5kU19kZW5vdm9fZmVycmV0JHN5bikKZE5kU19kZW5vdm9fZmVycmV0JGROZFMgPSBhcy5udW1lcmljKGROZFNfZGVub3ZvX2ZlcnJldCRkTmRTKQoKZE5kU19kZW5vdm9fZmVycmV0X3Bsb3QgPSBnZ3Bsb3QoZE5kU19kZW5vdm9fZmVycmV0LCBhZXMoeCA9IERQSSwgeSA9IGROZFMsIGNvbG9yID0gZmVycmV0SUQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gZmVycmV0SUQpKSArCiAgZmFjZXRfZ3JpZCh+ZGlldCtpbmZfcm91dGUpICsKICBQbG90VGhlbWUxIApwcmludChkTmRTX2Rlbm92b19mZXJyZXRfcGxvdCkKZ2dzYXZlKCJkTmRTX2Rlbm92b19mZXJyZXQucGRmIiwgZE5kU19kZW5vdm9fZmVycmV0X3Bsb3QsIHBhdGggPSBzYXZlZGlyKQpnZ3NhdmUoImROZFNfZGVub3ZvX2ZlcnJldC5wbmciLCBkTmRTX2Rlbm92b19mZXJyZXRfcGxvdCwgcGF0aCA9IHNhdmVkaXIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDUpCmBgYAoKYGBge3J9CiMgYnkgZmVycmV0IGFuZCBnZW5lCmROZFNfZGVub3ZvX2ZlcnJldF9nZW5lID0gZGlldHVuaXF1ZSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShmZXJyZXRJRCxEUEksZGlldCxpbmZfcm91dGUsc2VnbWVudCkgJT4lIAogIGNvdW50KG5vbnN5bikKCmROZFNfZGVub3ZvX2ZlcnJldF9nZW5lID0gcGl2b3Rfd2lkZXIoZE5kU19kZW5vdm9fZmVycmV0X2dlbmUsbmFtZXNfZnJvbSA9IG5vbnN5biwgdmFsdWVzX2Zyb20gPSBuKQpkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZSA9IHNlbGVjdChkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZSwgZmVycmV0SUQsRFBJLG5vbnN5bixzeW4pCmROZFNfZGVub3ZvX2ZlcnJldF9nZW5lJGROZFMgPSBwYXN0ZTAoZE5kU19kZW5vdm9fZmVycmV0X2dlbmUkbm9uc3luIC8gZE5kU19kZW5vdm9fZmVycmV0X2dlbmUkc3luKQpkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZSRkTmRTID0gYXMubnVtZXJpYyhkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZSRkTmRTKQoKZE5kU19kZW5vdm9fZmVycmV0X2dlbmVfcGxvdCA9IGdncGxvdChkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZSwgYWVzKHggPSBEUEksIHkgPSBkTmRTLCBjb2xvciA9IGRpZXQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gZmVycmV0SUQpKSArCiAgZmFjZXRfZ3JpZChzZWdtZW50fmRpZXQraW5mX3JvdXRlKSArCiAgUGxvdFRoZW1lMSArCiAgRGlldGNvbFNjYWxlCnByaW50KGROZFNfZGVub3ZvX2ZlcnJldF9nZW5lX3Bsb3QpCmdnc2F2ZSgiZE5kU19kZW5vdm9fZmVycmV0X2dlbmVfcGxvdC5wZGYiLCBkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZV9wbG90LCBwYXRoID0gc2F2ZWRpcikKZ2dzYXZlKCJkTmRTX2Rlbm92b19mZXJyZXRfZ2VuZV9wbG90LnBuZyIsIGROZFNfZGVub3ZvX2ZlcnJldF9nZW5lX3Bsb3QsIHBhdGggPSBzYXZlZGlyKQpgYGAKCmBgYHtyfQojZ2dwbG90KGZpbHRlcihkaWV0dW5pcXVlLCB0b3RhbHNhbXAgPiAxKSwgYWVzKHggPSBEUEksIHkgPSBtaW5vcmZyZXEsIGNvbG9yID0gbm9uc3luKSkgKwojICBnZW9tX3BvaW50KCkgKwojICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gdmFyKSkgKwojICBmYWNldF9ncmlkKGZlcnJldElEfmRpZXQraW5mX3JvdXRlKSArCiMgIFBsb3RUaGVtZTEgCmBgYAoKYGBge3J9Cm5vbnN5bnMgPSBmaWx0ZXIoZGlldHVuaXF1ZSwgbm9uc3luID09ICJub25zeW4iICYgdG90YWxzYW1wID4gMSkgJT4lIHVuZ3JvdXAoKSAlPiUgZHJvcGxldmVscygpCm5vbnN5bnMgPSBub25zeW5zWyFkdXBsaWNhdGVkKG5vbnN5bnMkdmFyKSxdIAoKbm9uc3lucyA9IHNlcGFyYXRlKG5vbnN5bnMsc2VnbWVudCwgaW50byA9IGMoInN0cmFpbiIsIkNIUk9NIikpCm5vbnN5bnMkbnR2YXIgPSBwYXN0ZTAobm9uc3lucyRtYWpvcixub25zeW5zJG50cG9zLG5vbnN5bnMkbWlub3IpCm5vbnN5bnMkYWF2YXIgPSBwYXN0ZTAobm9uc3lucyRtYWpvcmFhLG5vbnN5bnMkYWFwb3Msbm9uc3lucyRtaW5vcmFhKQoKbm9uc3luc19zbW9sID0gc2VsZWN0KG5vbnN5bnMsQ0hST00sbnR2YXIsYWF2YXIsZGlldCx0b3RhbHNhbXApICU+JSBkcm9wbGV2ZWxzKCkKd3JpdGUuY3N2KG5vbnN5bnNfc21vbCwgIm5vbnN5bnMuY3N2IikKYGBgCgpXaGljaCBmZXJyZXRzIGhhdmUgbW9yZSB0aGFuIG9uZSBkZSBub3ZvPwpgYGB7cn0KZ2dwbG90KGRpZXR1bmlxdWUsIGFlcyh4ID0gbnRwb3MsIHkgPSBmZXJyZXRJRCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRpZXQpKSArCiAgZmFjZXRfZ3JpZCh+c2VnbWVudCkgKwogIFBsb3RUaGVtZTEgKwogIERpZXRjb2xTY2FsZQoKZ2dwbG90KGZpbHRlcihkaWV0dW5pcXVlLCB0b3RhbHNhbXAgPjEpLCBhZXMoeCA9IG50cG9zLCB5ID0gZmVycmV0SUQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkaWV0KSkgKwogIGZhY2V0X2dyaWQofnNlZ21lbnQpICsKICBQbG90VGhlbWUxICsKICBEaWV0Y29sU2NhbGUKYGBgCgpBRiBvZiBzaGFyZWQgZGUgbm92b3MKYGBge3J9CmdncGxvdChmaWx0ZXIoZGlldHVuaXF1ZSwgdG90YWxzYW1wID4xKSwgYWVzKHggPSBEUEksIHkgPSBtaW5vcmZyZXEpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSB2YXIpKSArCiAgZmFjZXRfZ3JpZCh+ZmVycmV0SUQpICsKICBQbG90VGhlbWUxCmBgYAoKU05WcyBzaGFyZWQgYmV0d2VlbiBkaWV0IGdyb3VwcwpgYGB7cn0Kc2hhcmVkID0gZmVycmV0cyAlPiUgCiAgZmlsdGVyKHZhciAlaW4lIG9fdmFyKSAlPiUgCiAgZmlsdGVyKHZhciAlaW4lIGxfdmFyKSAlPiUgCiAgZ3JvdXBfYnkodmFyKSAlPiUgCiAgbXV0YXRlKGNvdW50ID0gMSwgdG90YWxzYW1wID0gc3VtKGNvdW50KSkKClNoYXJlZFBsb3QgPSBnZ3Bsb3Qoc2hhcmVkLCBhZXMoeCA9IG50cG9zLCB5ID0gc2VnbWVudCkpICsKICBnZW9tX3BvaW50KGFlcyhzaXplID0gdG90YWxzYW1wLCBjb2xvciA9IG5vbnN5bikpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2Ygc2FtcGxlcyBjb250YWluaW5nIGVhY2ggdmFyaWFudCAtIFNoYXJlZCBiZXR3ZWVuIGRpZXQgZ3JvdXBzIikgKwogIHRoZW1lKGxlZ2VuZC5rZXkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIiksCiAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkgKwogIFBsb3RUaGVtZTEKcHJpbnQoU2hhcmVkUGxvdCkKZ2dzYXZlKFNoYXJlZFBsb3QsIGZpbGVuYW1lID0gIlNlZ21lbnRTTlZQbG90X0RpZXRTaGFyZWQucGRmIiwgcGF0aCA9IHNhdmVkaXIpCmBgYAoKYGBge3J9Ck1pbm9yZnJlcV9kaXN0ID0gZ2dwbG90KGZlcnJldHMsIGFlcyh4ID0gbWlub3JmcmVxLCBmaWxsID0gZGlldCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEpICsKICBQbG90VGhlbWUxICsKICBmYWNldF9ncmlkKGluZl9yb3V0ZX5kaWV0KSArCiAgRGlldGNvbFNjYWxlX2ZpbGwKcHJpbnQoTWlub3JmcmVxX2Rpc3QpCmdnc2F2ZSgiTWlub3JmcmVxX2Rpc3QucGRmIiwgTWlub3JmcmVxX2Rpc3QsIHBhdGggPSBzYXZlZGlyKQojIG9iZXNlIHNlZW0gdG8gaGF2ZSBmZXdlciBsb3ctZnJlcXVlbmN5IGRlIG5vdm8gU05WcwpgYGAKCmBgYHtyfQpvYmVzZV9pbmRleCA9IGZpbHRlcihmZXJyZXRzLCBkaWV0ID09ICJPYmVzZSIgJiBpbmZfcm91dGUgPT0gIkluZGV4IikgJT4lIHVuZ3JvdXAoKQpsZWFuX2luZGV4ID0gZmlsdGVyKGZlcnJldHMsIGRpZXQgPT0gIkxlYW4iICYgaW5mX3JvdXRlID09ICJJbmRleCIpICU+JSB1bmdyb3VwKCkKdC50ZXN0KG9iZXNlX2luZGV4JG1pbm9yZnJlcSwgbGVhbl9pbmRleCRtaW5vcmZyZXEpCiMgbWVhbnMgYXJlIG5vdCBkaWZmZXJlbnQKCm9iZXNlX2NvbnRhY3QgPSBmaWx0ZXIoZmVycmV0cywgZGlldCA9PSAiT2Jlc2UiICYgaW5mX3JvdXRlID09ICJDb250YWN0IikgJT4lIHVuZ3JvdXAoKQpsZWFuX2NvbnRhY3QgPSBmaWx0ZXIoZmVycmV0cywgZGlldCA9PSAiTGVhbiIgJiBpbmZfcm91dGUgPT0gIkNvbnRhY3QiKSAlPiUgdW5ncm91cCgpCnQudGVzdChvYmVzZV9jb250YWN0JG1pbm9yZnJlcSwgbGVhbl9jb250YWN0JG1pbm9yZnJlcSkKIyBtZWFucyBhcmUgbm90IGRpZmZlcmVudAoKIyBRUV9QbG90OiBjb21wYXJlcyB0aGUgcXVhbnRpbGVzIG9mIHR3byBkaXN0cmlidXRpb25zLCB4ID15IHN1Z2dlc3RzIHRoZXkgYXJlIGRyYXduIGZyb20gdGhlIHNhbWUgZGlzdHJpYnV0aW9uCnFxbm9ybShvYmVzZV9pbmRleCRtaW5vcmZyZXEsIG1haW4gPSAiT2Jlc2UgSW5kZXggLSBUZXN0IG9mIE5vcm1hbCBEaXN0cmlidXRpb24iKQpxcW5vcm0obGVhbl9pbmRleCRtaW5vcmZyZXEsIG1haW4gPSAiTGVhbiBJbmRleCAtIFRlc3Qgb2YgTm9ybWFsIERpc3RyaWJ1dGlvbiIpCiMgbmVpdGhlciBkaXN0cmlidXRpb24gaXMgbm9ybWFsCnFxcGxvdChvYmVzZV9pbmRleCRtaW5vcmZyZXEsbGVhbl9pbmRleCRtaW5vcmZyZXEsIHhsYWIgPSAiT2Jlc2UgSW5kZXgiLCB5bGFiID0gIkxlYW4gSW5kZXgiKQoKcXFub3JtKG9iZXNlX2NvbnRhY3QkbWlub3JmcmVxLCBtYWluID0gIk9iZXNlIENvbnRhY3QgLSBUZXN0IG9mIE5vcm1hbCBEaXN0cmlidXRpb24iKQpxcW5vcm0obGVhbl9jb250YWN0JG1pbm9yZnJlcSwgbWFpbiA9ICJMZWFuIENvbnRhY3QgLSBUZXN0IG9mIE5vcm1hbCBEaXN0cmlidXRpb24iKQojIG5laXRoZXIgZGlzdHJpYnV0aW9uIGlzIG5vcm1hbApxcXBsb3Qob2Jlc2VfY29udGFjdCRtaW5vcmZyZXEsbGVhbl9jb250YWN0JG1pbm9yZnJlcSwgeGxhYiA9ICJPYmVzZSBDb250YWN0IiwgeWxhYiA9ICJMZWFuIENvbnRhY3QiKQoKIyBNYW5uLVdoaXRuZXktV2lsY294IHRlc3QgKE1hbm4tV2hpdG5leSBVIHRlc3QpOiBzYW1wbGVzIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIGluZGVwZW5kZW50IG9mIGVhY2ggb3RoZXIKd2lsY294LnRlc3Qob2Jlc2VfaW5kZXgkbWlub3JmcmVxLGxlYW5faW5kZXgkbWlub3JmcmVxKQp3aWxjb3gudGVzdChvYmVzZV9jb250YWN0JG1pbm9yZnJlcSxsZWFuX2NvbnRhY3QkbWlub3JmcmVxKQojIGRpc3RyaWJ1dGlvbnMgYXJlIG5vdCBkaWZmZXJlbnQKCiMgS29sbW9nb3Jvdi1TbWlybm92IHRlc3Q6IHNhbXBsZXMgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCBhbmQgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlcgojICJzZW5zaXRpdmUgdG8gZGlmZmVyZW5jZXMgaW4gbG9jYXRpb24gYW5kIHNoYXBlIG9mIHRoZSBlbXBpcmljYWwgQ0RGcyBvZiB0aGUgdHdvIHNhbXBsZXMiCmtzLnRlc3Qob2Jlc2VfaW5kZXgkbWlub3JmcmVxLGxlYW5faW5kZXgkbWlub3JmcmVxKQprcy50ZXN0KG9iZXNlX2NvbnRhY3QkbWlub3JmcmVxLGxlYW5fY29udGFjdCRtaW5vcmZyZXEpCiMgZGlzdHJpYnV0aW9ucyBhcmUgbm90IGRpZmZlcmVudApgYGA=